import Vue from 'vue';

/**
 * 权限指令。在需要控制权限的组件上使用, 当前用户没有权限时，使用了该指令的组件则会被删除或隐藏。
 * 它依赖于权限树，权限树是当前用户拥有的所有权限组成的树。
 * 
 * 用法示例
 *  <button v-auth:delete>删除</button>
 *  <button v-auth="'/center/user/add'">增加</button>
 * 这样当当前用户没有'/center/user/add'权限时，将不会显示增加按钮。
 * 注意，用在<template>上无效。
 * 
 * v-auth的参数和表达式
 *   因为权限数据是树形结构，所以我们通过'/'符号来表示权限节点层级，就像文件路径，例如 '/center/user/add'。
 *   如果开头字符就是'/'，则表示绝对路径而从根权限节点开始匹配，否则是相对路径而从 vnode.context.permission 开始匹配。
 *   可以是一个字符串数组，此时必须用表达式写法。
 *   可以使用通配符 '*'。
 * 
 * vnode.context.permission
 *   在组件的上下文上可以设置一个permission字段，它的值是字符串，表示权限的一个父权限。
 *   用法示例：
 *     在父组件的data里定义一个permission字段，例如：
 *       data() { return {permission: '/center/user'}; }
 *     然后你可以简写v-auth的表达式：
 *       <button v-auth:add>增加</button>
 * 
 * 注意
 *   v-auth判定一个权限是当前用户是拥有的，只是表示该权限在权限树是存在的所以是当前用户拥有的，并不表示该权限下的所有子权限也是都拥有的；
 *   但是判定一个权限是当前用户不拥有的，则该权限的子权限也一定不拥有。
 *   例如，假如 v-auth="'/center/user'"将判定为当前用户拥有，并不表示'/center/user/update'等权限当前用户也拥有。
 *   因为v-auth依赖权限数据，但是不知道依赖的权限数据是否早就已经被过滤过（它可能被后端的权限API根据当前用户权限过滤过）。
 */
Vue.directive('auth', {
  inserted(el, binding, vnode) {
    // 先隐藏元素
    const initialDisplay = el.style.display;
    el.style.display = 'none';

    if (_hasAuth(binding.value || binding.arg || '', vnode.context.permission)) {
      el.style.display = initialDisplay;
    } else {
      if (el.parentNode) {
        const child = el.parentNode.removeChild(el);
        if (!child) {
          el.style.display = 'none';
        }
      }
    }
  },
});

Vue.prototype.$auth = function (permissionPaths) {
  return _hasAuth(permissionPaths, this.$vnode.context.permission || this.permission);
}

/**
 *
 * @param permissionPath 要判断的权限
 * @param parentArg 
 */
function _hasAuth(permissionPaths, parentPath) {
    permissionPaths = (typeof permissionPaths == 'string' ? [permissionPaths] : permissionPaths);
    
    /// 构造匹配权限路径
    const SEPARATOR = '/';

    permissionPaths = permissionPaths.map((subPath) => {
      if (subPath && subPath[0] == SEPARATOR) {
        // 如果是绝对路径
        return subPath.substring(1).split(SEPARATOR);
      } else {
        // 如果是相对路径
        if (parentPath) {
          const joinPath = (path1, path2) => path1 && path2
            ? path1 + SEPARATOR + path2
            : path1 ? path1 : path2;
          parentPath = parentPath[0] == SEPARATOR ? parentPath.substring(1) : parentPath;
          return joinPath(parentPath, subPath).split(SEPARATOR);
        } else {
          return subPath.split(SEPARATOR);
        }
      }
    });

    // permissionPaths为要匹配的多个路径的数组
    const match = (permissions, paths, depth) => {
      for (let path of paths) {
        for (let permission of permissions) {
          if (path[depth] == '*' || path[depth] == permission.code) {
            // 如果当前层级节点匹配，并且已经完成深度遍历
            if (path.length - 1 == depth) {
              return true;
            } else {
              // 还未完成深度遍历，继续匹配下一层级节点
              if (permission.children) {
                const matched = match(permission.children, paths, depth + 1);
                if (matched) {
                  return true;
                } else {
                  // 如果下一层级节点不匹配，可以立即判定为当前path整个不匹配
                  break;
                }
              } else {
                // 如果没有下一层级节点，可以立即判定为当前path整个不匹配
                break;
              }
            }
          }
        }
      }
      return false;
  };

  return match(app.admin.permissions, permissionPaths, 0);
}

export function formatPermissions(nodes) {
  let permissions = [];
  nodes.forEach(node => {
    let permission = {
      id: node.id,
      isMenu: node.attributes && !!node.attributes.isMenu,
      code: (node.attributes && node.attributes.code) || node.code,
      pagePath: node.attributes && node.attributes.url,
      text: node.text
    };
    permissions.push(permission);

    if (node.children) {
      permission.children = formatPermissions(node.children, permission);
    }
  });
  return permissions;
};